package android.bluebox.model;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

public class NetworkBox {

	private final String VERIFYPC_1 = "2973";
	private final String VERYFYPC_2 = "6593";
	private final String REQUESTCONNECTING = "2872";
	private final String RESPONECONNECTING = "1103";
	private final String REQUESTPAIRCODE = "3943";
	private final String RESPONSEPAIRCODE = "5292";
	public final String SENDMESSAGE = "1555";
	private final String ENSURE = "1457";
	private final String LOGOUT = "9999";
	private final String IEREQUEST = "2211";
	private final String IERESPONSE = "2218";

	private final int MODNUMBER = 127;

	private final String LOGINWINDOWS = "loginwindows";
	private final String BROWSER = "browser";
	public final String MISC = "misc";

	private  String PAIRCODE = "";

	private final String SEPERATECHAR = "$";
	private final String SPLIT = "\\" + SEPERATECHAR;

	private int hostPort = 7777;
	private String hostIP = null;
	private String sessionId;

	private InetAddress host;
	private Socket socket;
	private DataInputStream dis;
	private DataOutputStream dos;

	public void setHostIP(String IP) {
		hostIP = IP;
	}

	public boolean checkIP(String IP) {
		return checkIP(IP, hostPort);
	}

	public boolean checkIP(String IP, int port) {
		try {
			host = InetAddress.getByName(IP);
			socket = new Socket(host, port);

			socket.setSoTimeout(5000);

			if (!socket.isConnected()) 
				return false;

			dos = new DataOutputStream(socket.getOutputStream());
			dis = new DataInputStream(socket.getInputStream());

			return true;
		} catch (UnknownHostException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
	}

	public boolean disconect() {
		if (!checkIP(hostIP))
			return false;

		try {
			sendRawMessage(StaticBox.paircodeCrypto.encrypt(sessionId + SEPERATECHAR + LOGOUT));
			if (dos!= null) dos.close();
			if (dis!= null) dis.close();
			return true;
		} catch (IOException e) {
		}

		return false;
	}

	public boolean findAvailableWorkspace(String IP) {

		if (!checkIP(IP))
			return false;

		sendRawMessage(SEPERATECHAR + VERIFYPC_1);
		String message = recieveRawMessage();
		return (message != null && message.trim().equals(SEPERATECHAR + VERYFYPC_2));
	}

	public boolean requestToConnect(String hostIP) {

		if (!checkIP(hostIP))
			return false;

		sendRawMessage(SEPERATECHAR + REQUESTCONNECTING);
		String message = recieveRawMessage();
		return (message != null && message.trim().equals(SEPERATECHAR + RESPONECONNECTING));
	}

	private boolean sendRawMessage(String str) {
		return sendRawMessageWithoutEOF(str + "<EOF>");
	}

	private boolean sendRawMessageWithoutEOF(String str) {
		try {
			dos.writeBytes(str);
			dos.flush();
			return true;
		} catch (IOException e) {
		}

		return false;
	}

	private boolean sendMessage(String _sessionId, String _type, String _tag, ArrayList<AValue> _aValueList) {

		if (!checkIP(hostIP))
			return false;

		String message = "";
		message += _sessionId + SEPERATECHAR;
		message += _type + SEPERATECHAR;
		message += "tag:" + _tag + SEPERATECHAR;
		int number = _aValueList.size();
		for (int index = 0; index < number; index++) {
			AValue avalue = _aValueList.get(index);
			message += StaticBox.dbBox.getAttribute(avalue.getAttrId()).getName() + ":" + avalue.getName();
			if (index != number - 1)
				message += SEPERATECHAR;
			StaticBox.dbBox.updateWorkspaceAValue(avalue.getId());
		}
		return sendRawMessage(StaticBox.paircodeCrypto.encrypt(message));
	}

	public boolean sendMessage(String _type, String _tag, ArrayList<AValue> _aValueList) {
		return sendMessage(sessionId, _type, _tag, _aValueList);
	}

	public String recieveRawMessage() {
		try {
			byte[] b = new byte[1024];
			dis.read(b);
			String s = new String (b);
			return s;
		} catch (UnknownHostException e) {
		} catch (IOException e) {
		}
		return null;
	}

	public boolean verifyPairCode(String str, String IP) {
		if (!checkIP(IP))
			return false;

		/*
		 * @moriator
		 * need salt
		 */

		String md5 = Crypto3.createMD5(str.toUpperCase());

		sendRawMessage(SEPERATECHAR + REQUESTPAIRCODE + SEPERATECHAR + md5);

		String message = recieveRawMessage();

		if (message != null) {
			StaticBox.paircodeCrypto = new PasswordCrypto();
			StaticBox.paircodeCrypto.setPassword(md5);
			String deMessage = StaticBox.paircodeCrypto.decrypt(message);
			String[] deMessages = deMessage.split(SPLIT);
			if (deMessages[1].equals(RESPONSEPAIRCODE)) {
				sessionId = deMessages[0];
				sendRawMessage((sessionId + SEPERATECHAR + ENSURE));
				hostIP = IP;
				return true;
			}
		}
		return false;
	}

	public String getPairCode() {
		return PAIRCODE;
	}

	public boolean sendMessageLoginWindow(int _paircodeNumber, ArrayList<AValue> _aValueList) {
		String message = SEPERATECHAR + SENDMESSAGE + SEPERATECHAR + LOGINWINDOWS + SEPERATECHAR;

		StaticBox.dbBox.updateWorkspaceAValue(_aValueList.get(0).getId());
		StaticBox.dbBox.updateWorkspaceAValue(_aValueList.get(1).getId());
		
		String username, password;
		if (StaticBox.dbBox.getAttribute(_aValueList.get(0).getAttrId()).getName().equals("username")) {
			username = getEncryptStringForLoginWindow(_paircodeNumber, _aValueList.get(0).getName());
			password = getEncryptStringForLoginWindow(_paircodeNumber, _aValueList.get(1).getName());

		} else {
			username = getEncryptStringForLoginWindow(_paircodeNumber, _aValueList.get(1).getName());
			password = getEncryptStringForLoginWindow(_paircodeNumber, _aValueList.get(0).getName());
		}

		message += username + SEPERATECHAR + password;
		return sendRawMessageWithoutEOF(message);
	}

	public boolean loginWindows(String _hostIP, String _paircode) {
		if (!checkIP(_hostIP, hostPort + 10))
			return false;
		int s = getPaircodeNumber(_paircode);
		ArrayList<AValue> aValueList = StaticBox.dbBox.filterAValueByTag(LOGINWINDOWS);
		return sendMessageLoginWindow(s, aValueList);
	}

	public int getPaircodeNumber(String _paircode) {
		byte[] pc = _paircode.getBytes();
		int s = 0;
		for (int i = 0; i < pc.length; i++) {
			s += pc[i];
		}
		return s % MODNUMBER;
	}

	public String getEncryptStringForLoginWindow(int _paircodeNumber, String _str) {
		byte[] bStr = _str.getBytes();
		for (int i = 0; i < bStr.length; i++) {
			int temp = bStr[i] + _paircodeNumber;
			bStr[i]  = (byte) (temp % MODNUMBER + 1);
		}
		return new String(bStr);
	}

	public boolean AutoFillBrowser() {
		if (!checkIP(hostIP, hostPort))
			return false;
		sendRawMessage(StaticBox.paircodeCrypto.encrypt(sessionId + SEPERATECHAR + IEREQUEST));
		String message = recieveRawMessage();

		if (message == null)
			return false;

		String[] deMessages = StaticBox.paircodeCrypto.decrypt(message).split(SPLIT);
		ArrayList<String> deMessagesList = new ArrayList<String>();

		if (!sessionId.equals(deMessages[0]) || !deMessages[1].equals(IEREQUEST))
			return false;

		String tagName = new String(Base64.decode(deMessages[2].substring(4), Base64.DEFAULT));

		ArrayList<AValue> aValueList1 = StaticBox.dbBox.filterAValueByTag(tagName);
		if (aValueList1 == null)
			return false;
		ArrayList<AValue> aValueList2 = new ArrayList<AValue>(); 
		for (int i = 3; i < deMessages.length; i++) {
			int attrId = StaticBox.dbBox.getSynonyms(deMessages[i]);
			if (attrId >= 0) {
				deMessagesList.add(deMessages[i]);
				for (AValue avalue : aValueList1) {
					if (avalue.getAttrId() == attrId) {
						aValueList2.add(avalue);
					}
				}
			}
		}

		return sendMessageForAutofill(deMessagesList, aValueList2);
	}

	private boolean sendMessageForAutofill(ArrayList<String> _attributeList, ArrayList<AValue> _aValueList) {

		if (!checkIP(hostIP))
			return false;

		String message = "";
		message += sessionId + SEPERATECHAR;
		message += IERESPONSE + SEPERATECHAR;
		message += "tag:" + BROWSER + SEPERATECHAR;
		int number = _aValueList.size();
		for (int index = 0; index < number; index++) {
			message += _attributeList.get(index) + ":" + _aValueList.get(index).getName();
			if (index != number - 1)
				message += SEPERATECHAR;
			StaticBox.dbBox.updateWorkspaceAValue(_aValueList.get(index).getId());
		}
		return sendRawMessage(StaticBox.paircodeCrypto.encrypt(message));
	}
}